﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ATEC.ATDataCoreNET;
using ATEC.GeometryNET;

namespace ATSceneExample
{
    /// <summary>
    /// 分类：玄镜BIM图形平台/C#开发/示例代码/场景管理/参考文件操作。
    /// 内容：示例代码展示了如何创建、删除、引用、缩放和旋转参考文件场景。
    /// 功能：实现对场景的管理与操作，包括嵌套引用、平行引用以及几何对象的动态修改。
    /// 关键类及说明：
    ///     ATProject: 表示当前活动的工程对象，用于获取和操作场景。
    ///     ATScene: 表示场景对象，可包含几何体和参考信息。
    ///     ATAttachment: 表示场景之间的引用关系，用于实现嵌套或平行引用。
    ///     XPoint3d, XVector3d: 分别表示点和向量的几何类。
    /// 关键方法：
    ///     AttachcScen: 将一个场景引用到目标场景中。
    ///     CreateGeometry: 在场景中创建几何体。
    ///     ScaleAttachment, RotateAttachment: 对场景的引用进行缩放或旋转操作。
    /// </summary>
    public class ATAttachmentExample
    {
        private const string mainSceneName = "三维模型空间"; // 主场景名称。
        private const int mainSceneViewNum = 2; // 主场景视图编号。

        // 嵌套引用的场景名称。
        private const string childNestedSceneName1 = "childNestedScene1"; // 嵌套场景1。
        private const string childNestedSceneName2 = "childNestedScene2"; // 嵌套场景2。
        private const string childNestedSceneName3 = "childNestedScene3"; // 嵌套场景3。

        // 平行引用的场景名称。
        private const string childParallelSceneName1 = "childParallelScene1"; // 平行场景1。
        private const string childParallelSceneName2 = "childParallelScene2"; // 平行场景2。
        private const string childParallelSceneName3 = "childParallelScene3"; // 平行场景3。

        /// <summary>
        /// 创建场景。
        /// 功能：为给定名称的场景数组依次创建新的场景对象，并在其中添加几何体。
        /// </summary>
        /// <param name="sceneNames">场景名称数组。</param>
        private static void CreateScens(string[] sceneNames)
        {
            ATProject activeProject = ATEC.ATCore.Session.Entity.GetActiveATProject(); // 获取当前活动工程。
            int sceneCount = sceneNames.Length; // 获取场景名称的数量。
            for (int i = 0; i < sceneCount; i++) // 遍历所有场景名称。
            {
                string sceneName = sceneNames[i]; // 当前场景名称。
                ATScenesStatus status; // 创建场景的状态。
                ATScene scene = activeProject.CreateNewScene(out status, sceneName, ATScenesType.Normal, true, null); // 创建新场景。
                if (status != ATScenesStatus.Success || !scene.IsValid()) // 如果场景创建失败或无效。
                    continue; // 跳过。

                CreateGeometry(scene, i); // 在场景中创建几何体。
            }
        }

        /// <summary>
        /// 创建几何对象。
        /// 功能：在指定场景中生成几何体，并将其添加到场景中。
        /// </summary>
        /// <param name="scene">目标场景对象。</param>
        /// <param name="sceneNo">场景编号，用于区分几何体位置。</param>
        private static void CreateGeometry(ATScene scene, int sceneNo)
        {
            if (scene == null || !scene.IsValid()) // 如果场景无效。
                return; // 直接返回。

            // 根据场景编号调整几何体位置。
            int x = sceneNo * 200; // 偏移值（暂未使用）。
            XPoint3d pt1 = XPoint3d.FromXYZ(0 + sceneNo * 5, 10, 10); // 顶点1。
            XPoint3d pt2 = XPoint3d.FromXYZ(0 + sceneNo * 5, 10, -10); // 顶点2。
            XPoint3d pt3 = XPoint3d.FromXYZ(0 + sceneNo * 5, -10, -10); // 顶点3。
            XPoint3d pt4 = XPoint3d.FromXYZ(0 + sceneNo * 5, -10, 10); // 顶点4。
            List<XPoint3d> pts = new List<XPoint3d> { pt1, pt2, pt3, pt4, pt1 }; // 构建顶点集合。

            // 创建几何对象的闭合曲线。
            CurveSet tempCurveSet = CurveSet.CreateLinear(pts, CurveSet.BoundaryType.Outer, true); // 创建闭合曲线。
            ATEC.ATDataCoreNET.ATObjects.ATObject obj = ATEC.ATDataCoreNET.ATObjects.GeometryToATObject.ToObject(scene.GetATScene(), tempCurveSet, null); // 将几何体转换为场景对象。
            obj.AddToScene(); // 添加几何体到场景。
        }

        /// <summary>
        /// 删除同名场景。
        /// 功能：从工程中删除指定名称的场景及其引用。
        /// </summary>
        /// <param name="sceneNames">场景名称数组。</param>
        public static void DeleteExistedScenes(string[] sceneNames)
        {
            ATProject activeProject = ATEC.ATCore.Session.Entity.GetActiveATProject(); // 获取当前活动工程。
            ATScene mainScene = ATSceneMethod.GetATScene(activeProject, mainSceneName); // 获取主场景。
            if (mainScene == null || !mainScene.IsValid()) // 如果主场景无效。
                return; // 直接返回。

            int sceneCount = sceneNames.Length; // 获取场景名称数量。
            for (int i = 0; i < sceneCount; i++) // 遍历场景名称。
            {
                ATAttachment attachment = GetAttachmentByName(sceneNames[i]); // 获取场景的引用。
                if (attachment != null) // 如果引用存在。
                    mainScene.DeleteATAttachment(attachment); // 从主场景中删除引用。

                ATScene scene = ATSceneMethod.GetATScene(activeProject, sceneNames[i]); // 获取场景对象。
                if (scene != null && scene.IsValid()) // 如果场景有效。
                    activeProject.DeleteScene(scene); // 删除场景。
            }

            // 保存更改。
            activeProject.ProcessChanges(ATSaveReason.ReferenceFileSave); // 提交更改。
        }

        /// <summary>
        /// 嵌套引用参考文件。
        /// 功能：实现场景之间的嵌套引用关系。
        /// </summary>
        public static void AttachcNestedScenes()
        {
            string[] sceneNames = { childNestedSceneName1, childNestedSceneName2, childNestedSceneName3 }; // 嵌套场景名称。
            DeleteExistedScenes(sceneNames); // 删除同名场景。
            CreateScens(sceneNames); // 创建嵌套场景。
            ATProject activeProject = ATEC.ATCore.Session.Entity.GetActiveATProject(); // 获取当前活动工程。
            string[] attachSceneNames = { childNestedSceneName3, childNestedSceneName2, childNestedSceneName1, mainSceneName }; // 引用场景的顺序。
            int sceneCount = attachSceneNames.Length; // 场景数量。

            // 为场景创建嵌套引用关系。
            for (int i = 1; i < sceneCount; i++) // 从第二个场景开始。
            {
                ATScene scene = ATSceneMethod.GetATScene(activeProject, attachSceneNames[i]); // 获取当前场景。
                if (scene == null || !scene.IsValid()) // 如果场景无效。
                    continue; // 跳过。

                AttachcScen(destScene: scene, activeProject, sceneToAttach: attachSceneNames[i - 1]); // 引用前一个场景。
            }
            SSDPNET.ATView.ViewUpdateSingle(mainSceneViewNum); // 更新主场景视图。
        }

        /// <summary>
        /// 平行参照参考文件。
        /// 创建场景后，将多个子场景以平行方式附加到主场景。
        /// </summary>
        public static void AttachcParallelScenes()
        {
            string[] sceneNames = { childParallelSceneName1, childParallelSceneName2, childParallelSceneName3 }; // 定义子场景名称数组.
            DeleteExistedScenes(sceneNames); // 删除已经存在的同名场景.
            CreateScens(sceneNames); // 创建新的子场景.
            ATProject activeProject = ATEC.ATCore.Session.Entity.GetActiveATProject(); // 获取当前活动项目.

            ATScene mainScene = ATSceneMethod.GetATScene(activeProject, mainSceneName); // 获取主场景对象.
            if (mainScene == null || !mainScene.IsValid()) // 检查主场景是否有效.
                return;

            // 为主场景附加子场景，按顺序添加平行参照.
            AttachcScen(mainScene, activeProject, childParallelSceneName1); // 添加第一个子场景.
            AttachcScen(mainScene, activeProject, childParallelSceneName2); // 添加第二个子场景.
            AttachcScen(mainScene, activeProject, childParallelSceneName3); // 添加第三个子场景.
            SSDPNET.ATView.ViewUpdateSingle(mainSceneViewNum); // 更新视图以反映变化.
        }

        /// <summary>
        /// 为目标场景附加参照场景。
        /// </summary>
        /// <param name="destScene">目标场景对象。</param>
        /// <param name="activeProject">当前活动的项目。</param>
        /// <param name="sceneToAttach">需要附加的场景名称。</param>
        public static void AttachcScen(ATSceneRef destScene, ATProject activeProject, string sceneToAttach)
        {
            if (destScene == null) // 检查目标场景是否为空.
                return;

            ATDocMoniker docMoniker = activeProject.GetDocument().GetMoniker(); // 获取文档标识符.
            ATAttachment attachment = destScene.CreateATAttachment(docMoniker, sceneToAttach); // 创建参照附件.
            if (attachment == null) // 检查附件是否创建成功.
                return;

            attachment.NestDepth = 99; // 设置嵌套深度为最大值.
            attachment.WriteToScene(false); // 将附件写入场景.
        }

        /// <summary>
        /// 根据名称获取场景的参照文件。
        /// </summary>
        /// <param name="sceneName">需要查找的场景名称。</param>
        /// <returns>对应的参照附件对象。</returns>
        public static ATAttachment GetAttachmentByName(string sceneName)
        {
            ATAttachment resAttachment = null; // 初始化结果为 null.
            ATProject activeProject = ATEC.ATCore.Session.Entity.GetActiveATProject(); // 获取当前活动项目.
            ATScene mainScene = ATSceneMethod.GetATScene(activeProject, mainSceneName); // 获取主场景对象.
            if (mainScene == null || !mainScene.IsValid()) // 检查主场景是否有效.
                return resAttachment;

            // 遍历主场景中的所有参照附件，查找匹配的场景名称.
            foreach (ATAttachment attachment in mainScene.GetATAttachments())
            {
                if (attachment.AttachSceneName.Equals(sceneName) && attachment != null) // 检查名称是否匹配.
                    resAttachment = attachment; // 更新结果为找到的附件.
            }

            return resAttachment; // 返回找到的附件.
        }

        /// <summary>
        /// 缩放参考文件。
        /// 如果未缩放，则放大两倍；否则恢复原始比例。
        /// </summary>
        public static void ScaleAttachment()
        {
            ATAttachment attachment = GetAttachmentByName(childParallelSceneName1); // 获取第一个子场景的附件.
            if (attachment == null) // 检查附件是否有效.
            {
                SSDPNET.ATOutputFuncs.OutputError("请先点击平行参照参考文件"); // 输出错误提示.
                return;
            }

            // 判断当前缩放值，如果是1则设置为2，否则恢复为1.
            double newScale = ((int)attachment.StoredScale == 1) ? 2 : 1; // 根据当前缩放值计算新值.
            attachment.StoredScale = newScale; // 设置新的缩放值.
            attachment.WriteToScene(false); // 将缩放变化写入场景.
            SSDPNET.ATView.ViewUpdateSingle(mainSceneViewNum); // 更新视图以显示缩放变化.
        }

        /// <summary>
        /// 旋转参考文件。
        /// 如果未旋转，则旋转45度；否则恢复原始方向。
        /// </summary>
        public static void RotateAttachment()
        {
            ATAttachment attachment = GetAttachmentByName(childParallelSceneName2); // 获取第二个子场景的附件.
            if (attachment == null) // 检查附件是否有效.
            {
                SSDPNET.ATOutputFuncs.OutputError("请先点击平行参照参考文件"); // 输出错误提示.
                return;
            }

            XVector3d axis = XVector3d.FromXYZ(1, 0, 0); // 定义旋转轴为X轴.
                                                         // 判断当前旋转矩阵是否为单位矩阵，如果是则旋转45度，否则恢复为单位矩阵.
            XMatrix3d rotation = (attachment.GetRotation().IsIdentity())
                ? XMatrix3d.Rotation(axis, XAngle.PI * 0.25) // 旋转45度.
                : XMatrix3d.Identity; // 恢复为单位矩阵.
            attachment.SetXRotMatrix(rotation); // 应用旋转矩阵到附件.
            attachment.WriteToScene(false); // 将旋转变化写入场景.
            SSDPNET.ATView.ViewUpdateSingle(mainSceneViewNum); // 更新视图以显示旋转变化.
        }
    }
}
